Introducción
José es un diseñador de juegos de mesa. Crea las reglas, diseña los gráficos, escoge su tema, número de jugadores y duración promedio del juego que tiene en mente. José es una persona tímida, y a pesar de que sus juegos suelen gustarle a sus amigos, él nunca ha querido publicarlos por miedo a que no sean bien recibidos. Se quiere demostrar a José, con una base de datos de calificaciones históricas de juegos de mesa, cómo hubieran sido recibidos sus juegos en promedio en la época que los fue creando.
Los datos a utilizar vienen de esta base de datos: (board_games)* que, en cambio, vienen de la página Board Game Geek.
Instalación de Paquetes
Procedemos para empezar en instalar los siguientes paquetes, se puede omitir este paso si ya se tienen previamente instalados. Aquí una lista de los cuales vamos a necesitar.
#install.packages("DataExplorer")
#install.packages("lares")
Cargar Librerías
Usando ‘library’ cargamos las librerías, con las cuales vas a hacer uso de las diferentes funciones.
package 㤼㸱vtree㤼㸲 was built under R version 4.0.5
Ánalisis Descriptivo, Data Engineering
Leemos nuestro dataset
En este caso usamos read.csv, proceedemos a leer:
board_games <- read.csv("./board_games.csv")
Observación de las primeras líneas
Para ejemplificar usaremos el data frame turbines, y con la función head() solo mostraremos las pirmeras 6 líneas de este:
- game_id Identificador único
- description Descripción corta
- image URL con imagen del juego
- max_players Jugadores máximos
- max_playtime Tiempo máximo de juego
- min_age Edad mínima
- min_players Jugadores mínimos
- min_playtime Tiempo mínimo de juego
- name Nombre del juego
- playing_time Tiempo promedio de juego
- thumbnail URL con thumbnail del juego
- year_published Año de publicación
- artist Diseñador gráfico del juego
- category Categorías del juego (separadas por coma)
- compilation Si es parte de una compilación, nombre de la compilación
- designer Diseñador del juego
- expansion Si hay una expansión, el nombre de la expansión
- family Familia, equivalente a editora
- mechanic Mecánicas, separadas por coma
- publisher Compañía o persona que publicaron el juego (separadas por coma)
- average_rating Calificación promedio en Board Game Geek
- users_rated Número de usuarios que calificaron el juego
Colnames de nuestro dataset
Después de una rápida observación, ejecutamos los siguientes comandos para confirmación:
[1] "game_id" "description" "image" "max_players" "max_playtime"
[6] "min_age" "min_players" "min_playtime" "name" "playing_time"
[11] "thumbnail" "year_published" "artist" "category" "compilation"
[16] "designer" "expansion" "family" "mechanic" "publisher"
[21] "average_rating" "users_rated"
Tipo de variables
Usando data explorer observamos el tipo de variables, casi tenemos el mismo porcentaje para las discretas y continua, y tenemos un bajo porcentaje de missing values:
- Sólo el 0.99% de las filas están completas,
- tenemos 11.54% de observaciones faltantes, es decir, dado que solo tenemos 0.99% de las filas completas, solo hay 10.55% de observaciones faltantes del total.
Estos valores faltantes nos podrán general problemas para analizar los datos, veamos un poco los perfiles que faltan.

Missing plot
Para visualizar el perfil de los datos faltantes podemos utilizar la función plot_missing(). En la visualización debajo, podemos ver que la variables compilation y expansion, son las que les falta información, encontramos de que sólo el 2.63% (compilation), 16.54% (expansion) de nuestras filas estén completas y probablemente esta varible no sea de mucha infomación. Por tanto la podemos eliminar de nuestro dataframe, ahorita mismo!!
plot_missing(board_games)

Eliminamos la columna que tienes más missing values
Eliminamos notes de nuestro dataframe:
final_board_games <- drop_columns(board_games, c("compilation","expansion"))
colnames(final_board_games)
[1] "game_id" "description" "image" "max_players" "max_playtime"
[6] "min_age" "min_players" "min_playtime" "name" "playing_time"
[11] "thumbnail" "year_published" "artist" "category" "designer"
[16] "family" "mechanic" "publisher" "average_rating" "users_rated"
final_board_games <- na.omit(final_board_games)
Ánalisis de Correlación
Podemos ver la más alta correlación en estas variables:
- min_playtime-max_playtime
- min_playtime-min_age
- min_playtime-playing_time
- average_rating-min_age
plot_correlation(na.omit(final_board_games), maxcat = 5L)
Ignored all discrete features since `maxcat` set to 5 categories!

Ahora de una manera más detallada vamos a analizar las variables más correlacionadas entre si, este top de 10:
corr_cross(final_board_games, # name of dataset
max_pvalue = 0.05, # display only significant correlations (at 5% level)
top = 10 # display top 10 couples of variables (by correlation coefficient)
)
Returning only the top 10. You may override with the 'top' argument
`guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.

QQ plot
La gráfica Quantile-Quantile es una forma de visualizar la desvisión de una distribución de probabilidad específica.
Después de analizar estos gráficos, a menudo es beneficioso aplicar una transformación matemática (como logaritmo) para modelos como la regresión lineal. Para hacerlo, podemos usar la función plot_qq. De forma predeterminada, se compara con la distribución normal.
qq_data <- final_board_games[, c("min_playtime", "max_playtime", "min_age", "playing_time", "average_rating")]
plot_qq(qq_data, sampled_rows = 1000L)

En el gráfico, las columnas parecen sesgadas en ambas colas. Apliquemos una transformación logarítmica simple y grafiquemos de nuevo. Average Rating no mejoró. Gabo si ves que es necesario podemos hacerle update a las columnas con la transformación.
log_qq_data <- update_columns(qq_data, 1:5, function(x) log(x + 1))
plot_qq(log_qq_data, sampled_rows = 1000L)

Ánalisis Exploratorio de los Datos
Teniendo nuestras variables con mayor correlación vamos a gráficarlas con geom point..:
- min_playtime-max_playtime


- average_rating-playing_time

- users_rated-average_rating

###Using vtree para explorar
Usamos vtree para observar la concentración de los datos por ejemplo para min_age, donde la mayoría de los datos se concentran en min_age de 8 años, 10 años y 12 años.
Usamos vtree para observar la concentración de los datos por ejemplo para min_players, tenemos casi un 69% para min 2 jugadores y cerca del 19% para min 3 jugadores.
Usamos vtree para observar la concentración de los datos por ejemplo para max_players, tenemos casi un 23% para máx 4 jugadores y cerca del 25% para máx 6 jugadores.
Note that the echo = FALSE parameter was added to the code chunk to prevent printing of the R code that generated the plot.
LS0tDQp0aXRsZTogIkJvYXJkX0dhbWVzX1JlZ3Jlc3Npb25fUHJvamVjdCINCmF1dGhvcjogJ0FkcmlhbiBIb21lcm8gTW9yZW5vIEdhcmPDrWEtIGFkcmlhbi5tb3Jlbm9AaXRlc28ubXgsIEdhYnJpZWwgQWxlamFuZHJvIE1vcmFsZXMNCiAgUnVpei0gJw0KZGF0ZTogIjYvMjEvMjAyMSINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KICAgIGRmX3ByaW50OiBwYWdlZA0KICBnaXRodWJfZG9jdW1lbnQ6DQogICAgdG9jOiB5ZXMNCiAgICBkZXY6IGpwZWcNCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDogeWVzDQogICAgdGhlbWU6IGNvc21vDQogICAgaGlnaGxpZ2h0OiB0YW5nbw0KLS0tDQoNCmBgYHtyIHNldHVwLCBlY2hvID0gRkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobz0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoID0gNykNCmBgYA0KDQo8c3R5bGU+DQouZm9yY2VCcmVhayB7IC13ZWJraXQtY29sdW1uLWJyZWFrLWFmdGVyOiBhbHdheXM7IGJyZWFrLWFmdGVyOiBjb2x1bW47IH0NCjwvc3R5bGU+DQoNCjxjZW50ZXI+DQohW10oLi9pbWFnZXMvaXRlc28uanBlZyl7d2lkdGg9MjAlfQ0KDQoNCjwvY2VudGVyPg0KDQojIyBJbnRyb2R1Y2Npw7NuDQoNCkpvc8OpIGVzIHVuIGRpc2XDsWFkb3IgZGUganVlZ29zIGRlIG1lc2EuIENyZWEgbGFzIHJlZ2xhcywgZGlzZcOxYSBsb3MgZ3LDoWZpY29zLCBlc2NvZ2Ugc3UgdGVtYSwgbsO6bWVybyBkZSBqdWdhZG9yZXMgeSBkdXJhY2nDs24gcHJvbWVkaW8gZGVsIGp1ZWdvIHF1ZSB0aWVuZSBlbiBtZW50ZS4gSm9zw6kgZXMgdW5hIHBlcnNvbmEgdMOtbWlkYSwgeSBhIHBlc2FyIGRlIHF1ZSBzdXMganVlZ29zIHN1ZWxlbiBndXN0YXJsZSBhIHN1cyBhbWlnb3MsIMOpbCBudW5jYSBoYSBxdWVyaWRvIHB1YmxpY2FybG9zIHBvciBtaWVkbyBhIHF1ZSBubyBzZWFuIGJpZW4gcmVjaWJpZG9zLiBTZSBxdWllcmUgZGVtb3N0cmFyIGEgSm9zw6ksIGNvbiB1bmEgYmFzZSBkZSBkYXRvcyBkZSBjYWxpZmljYWNpb25lcyBoaXN0w7NyaWNhcyBkZSBqdWVnb3MgZGUgbWVzYSwgY8OzbW8gaHViaWVyYW4gc2lkbyByZWNpYmlkb3Mgc3VzIGp1ZWdvcyBlbiBwcm9tZWRpbyBlbiBsYSDDqXBvY2EgcXVlIGxvcyBmdWUgY3JlYW5kby4NCg0KTG9zIGRhdG9zIGEgdXRpbGl6YXIgdmllbmVuIGRlIGVzdGEgYmFzZSBkZSBkYXRvczogDQpbKGJvYXJkX2dhbWVzKV0oaHR0cHM6Ly9naXRodWIuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS90cmVlL21hc3Rlci9kYXRhLzIwMTkvMjAxOS0wMy0xMikqDQpxdWUsIGVuIGNhbWJpbywgdmllbmVuIGRlIGxhIHDDoWdpbmEgQm9hcmQgR2FtZSBHZWVrLg0KDQojIyBJbnN0YWxhY2nDs24gZGUgUGFxdWV0ZXMNCg0KUHJvY2VkZW1vcyBwYXJhIGVtcGV6YXIgZW4gaW5zdGFsYXIgbG9zIHNpZ3VpZW50ZXMgcGFxdWV0ZXMsIHNlIHB1ZWRlIG9taXRpciBlc3RlIHBhc28gc2kgeWEgc2UgdGllbmVuIHByZXZpYW1lbnRlIGluc3RhbGFkb3MuIEFxdcOtIHVuYSBsaXN0YSBkZSBsb3MgY3VhbGVzIHZhbW9zIGEgbmVjZXNpdGFyLg0KDQpgYGB7cn0NCiNpbnN0YWxsLnBhY2thZ2VzKCJEYXRhRXhwbG9yZXIiKQ0KI2luc3RhbGwucGFja2FnZXMoImxhcmVzIikNCiNpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikNCiNpbnN0YWxsLnBhY2thZ2VzKCJnZ3RoZW1lcyIpDQojaW5zdGFsbC5wYWNrYWdlcygidnRyZWUiKQ0KYGBgDQojIyBDYXJnYXIgTGlicmVyw61hcw0KIA0KVXNhbmRvICdsaWJyYXJ5JyBjYXJnYW1vcyBsYXMgbGlicmVyw61hcywgY29uIGxhcyBjdWFsZXMgdmFzIGEgaGFjZXIgdXNvIGRlIGxhcyBkaWZlcmVudGVzIGZ1bmNpb25lcy4gDQoNCmBgYHtyfQ0KbGlicmFyeSgiRGF0YUV4cGxvcmVyIikNCmxpYnJhcnkoImxhcmVzIikNCmxpYnJhcnkoImdncGxvdDIiKQ0KbGlicmFyeSgiZ2d0aGVtZXMiKQ0KbGlicmFyeSgidnRyZWUiKQ0KYGBgDQoNCiMjIMOBbmFsaXNpcyBEZXNjcmlwdGl2bywgRGF0YSBFbmdpbmVlcmluZw0KIA0KIyMjIExlZW1vcyBudWVzdHJvIGRhdGFzZXQNCg0KRW4gZXN0ZSBjYXNvIHVzYW1vcyByZWFkLmNzdiwgcHJvY2VlZGVtb3MgYSBsZWVyOg0KDQpgYGB7cn0NCmJvYXJkX2dhbWVzIDwtIHJlYWQuY3N2KCIuL2JvYXJkX2dhbWVzLmNzdiIpIA0KYGBgDQoNCiMjIyBPYnNlcnZhY2nDs24gZGUgbGFzIHByaW1lcmFzIGzDrW5lYXMNCg0KUGFyYSBlamVtcGxpZmljYXIgdXNhcmVtb3MgZWwgZGF0YSBmcmFtZSBgdHVyYmluZXNgLCB5IGNvbiBsYSBmdW5jacOzbiBgaGVhZCgpYCBzb2xvIG1vc3RyYXJlbW9zIGxhcyBwaXJtZXJhcyA2IGzDrW5lYXMgZGUgZXN0ZTogDQoNCi0gZ2FtZV9pZAlJZGVudGlmaWNhZG9yIMO6bmljbw0KLSBkZXNjcmlwdGlvbglEZXNjcmlwY2nDs24gY29ydGENCi0gaW1hZ2UJVVJMIGNvbiBpbWFnZW4gZGVsIGp1ZWdvDQotIG1heF9wbGF5ZXJzCUp1Z2Fkb3JlcyBtw6F4aW1vcw0KLSBtYXhfcGxheXRpbWUJVGllbXBvIG3DoXhpbW8gZGUganVlZ28NCi0gbWluX2FnZQlFZGFkIG3DrW5pbWENCi0gbWluX3BsYXllcnMJSnVnYWRvcmVzIG3DrW5pbW9zDQotIG1pbl9wbGF5dGltZQlUaWVtcG8gbcOtbmltbyBkZSBqdWVnbw0KLSBuYW1lCU5vbWJyZSBkZWwganVlZ28NCi0gcGxheWluZ190aW1lCVRpZW1wbyBwcm9tZWRpbyBkZSBqdWVnbw0KLSB0aHVtYm5haWwJVVJMIGNvbiB0aHVtYm5haWwgZGVsIGp1ZWdvDQotIHllYXJfcHVibGlzaGVkCUHDsW8gZGUgcHVibGljYWNpw7NuDQotIGFydGlzdAlEaXNlw7FhZG9yIGdyw6FmaWNvIGRlbCBqdWVnbw0KLSBjYXRlZ29yeQlDYXRlZ29yw61hcyBkZWwganVlZ28gKHNlcGFyYWRhcyBwb3IgY29tYSkNCi0gY29tcGlsYXRpb24JU2kgZXMgcGFydGUgZGUgdW5hIGNvbXBpbGFjacOzbiwgbm9tYnJlIGRlIGxhIGNvbXBpbGFjacOzbg0KLSBkZXNpZ25lcglEaXNlw7FhZG9yIGRlbCBqdWVnbw0KLSBleHBhbnNpb24JU2kgaGF5IHVuYSBleHBhbnNpw7NuLCBlbCBub21icmUgZGUgbGEgZXhwYW5zacOzbg0KLSBmYW1pbHkJRmFtaWxpYSwgZXF1aXZhbGVudGUgYSBlZGl0b3JhDQotIG1lY2hhbmljCU1lY8OhbmljYXMsIHNlcGFyYWRhcyBwb3IgY29tYQ0KLSBwdWJsaXNoZXIJQ29tcGHDscOtYSBvIHBlcnNvbmEgcXVlIHB1YmxpY2Fyb24gZWwganVlZ28gKHNlcGFyYWRhcyBwb3IgY29tYSkNCi0gYXZlcmFnZV9yYXRpbmcJQ2FsaWZpY2FjacOzbiBwcm9tZWRpbyBlbiBCb2FyZCBHYW1lIEdlZWsNCi0gdXNlcnNfcmF0ZWQJTsO6bWVybyBkZSB1c3VhcmlvcyBxdWUgY2FsaWZpY2Fyb24gZWwganVlZ28NCg0KYGBge3J9DQpoZWFkKGJvYXJkX2dhbWVzKQ0KYGBgDQoNCiMjIyBDb2xuYW1lcyBkZSBudWVzdHJvIGRhdGFzZXQNCg0KRGVzcHXDqXMgZGUgdW5hIHLDoXBpZGEgb2JzZXJ2YWNpw7NuLCBlamVjdXRhbW9zIGxvcyBzaWd1aWVudGVzIGNvbWFuZG9zIHBhcmEgY29uZmlybWFjacOzbjoNCg0KYGBge3J9DQpjb2xuYW1lcyhib2FyZF9nYW1lcykNCmBgYA0KDQojIyMgVGlwbyBkZSB2YXJpYWJsZXMNCg0KVXNhbmRvIGRhdGEgZXhwbG9yZXIgb2JzZXJ2YW1vcyBlbCB0aXBvIGRlIHZhcmlhYmxlcywgY2FzaSB0ZW5lbW9zIGVsIG1pc21vIHBvcmNlbnRhamUgcGFyYSBsYXMgZGlzY3JldGFzIHkgY29udGludWEsIHkgdGVuZW1vcyB1biBiYWpvIHBvcmNlbnRhamUgZGUgbWlzc2luZyB2YWx1ZXM6DQoNCi0gU8OzbG8gZWwgMC45OSUgZGUgbGFzIGZpbGFzIGVzdMOhbiBjb21wbGV0YXMsDQotIHRlbmVtb3MgMTEuNTQlIGRlIG9ic2VydmFjaW9uZXMgZmFsdGFudGVzLCBlcyBkZWNpciwgZGFkbyBxdWUgc29sbyB0ZW5lbW9zIDAuOTklIGRlIGxhcyBmaWxhcyBjb21wbGV0YXMsIHNvbG8gaGF5IDEwLjU1JSBkZSBvYnNlcnZhY2lvbmVzIGZhbHRhbnRlcyBkZWwgdG90YWwuDQoNCkVzdG9zIHZhbG9yZXMgZmFsdGFudGVzIG5vcyBwb2Ryw6FuIGdlbmVyYWwgcHJvYmxlbWFzIHBhcmEgYW5hbGl6YXIgbG9zIGRhdG9zLCB2ZWFtb3MgdW4gcG9jbyBsb3MgcGVyZmlsZXMgcXVlIGZhbHRhbi4NCg0KYGBge3IgYmFycGxvdH0NCnBsb3RfaW50cm8oYm9hcmRfZ2FtZXMpDQpgYGANCg0KIyMjIE1pc3NpbmcgcGxvdA0KDQpQYXJhIHZpc3VhbGl6YXIgZWwgcGVyZmlsIGRlIGxvcyBkYXRvcyBmYWx0YW50ZXMgcG9kZW1vcyB1dGlsaXphciBsYSBmdW5jacOzbiBwbG90X21pc3NpbmcoKS4gRW4gbGEgdmlzdWFsaXphY2nDs24gZGViYWpvLCBwb2RlbW9zIHZlciBxdWUgbGEgdmFyaWFibGVzIGNvbXBpbGF0aW9uIHkgZXhwYW5zaW9uLCBzb24gbGFzIHF1ZSBsZXMgZmFsdGEgaW5mb3JtYWNpw7NuLCBlbmNvbnRyYW1vcyBkZSBxdWUgc8OzbG8gZWwgMi42MyUgKGNvbXBpbGF0aW9uKSwgMTYuNTQlIChleHBhbnNpb24pIGRlIG51ZXN0cmFzIGZpbGFzIGVzdMOpbiBjb21wbGV0YXMgeSBwcm9iYWJsZW1lbnRlIGVzdGEgdmFyaWJsZSBubyBzZWEgZGUgbXVjaGEgaW5mb21hY2nDs24uIFBvciB0YW50byBsYSBwb2RlbW9zIGVsaW1pbmFyIGRlIG51ZXN0cm8gZGF0YWZyYW1lLCBhaG9yaXRhIG1pc21vISENCg0KYGBge3J9DQpwbG90X21pc3NpbmcoYm9hcmRfZ2FtZXMpDQpgYGANCg0KIyMjIEVsaW1pbmFtb3MgbGEgY29sdW1uYSBxdWUgdGllbmVzIG3DoXMgbWlzc2luZyB2YWx1ZXMNCg0KRWxpbWluYW1vcyBub3RlcyBkZSBudWVzdHJvIGRhdGFmcmFtZToNCg0KYGBge3J9DQpmaW5hbF9ib2FyZF9nYW1lcyA8LSBkcm9wX2NvbHVtbnMoYm9hcmRfZ2FtZXMsIGMoImNvbXBpbGF0aW9uIiwiZXhwYW5zaW9uIikpDQoNCmNvbG5hbWVzKGZpbmFsX2JvYXJkX2dhbWVzKQ0KYGBgDQoNCg0KYGBge3J9DQpmaW5hbF9ib2FyZF9nYW1lcyA8LSBuYS5vbWl0KGZpbmFsX2JvYXJkX2dhbWVzKSANCmBgYA0KDQojIyMgw4FuYWxpc2lzIGRlIENvcnJlbGFjacOzbg0KDQpQb2RlbW9zIHZlciBsYSBtw6FzIGFsdGEgY29ycmVsYWNpw7NuIGVuIGVzdGFzIHZhcmlhYmxlczoNCg0KLSBtaW5fcGxheXRpbWUtbWF4X3BsYXl0aW1lDQotIG1pbl9wbGF5dGltZS1taW5fYWdlDQotIG1pbl9wbGF5dGltZS1wbGF5aW5nX3RpbWUNCi0gYXZlcmFnZV9yYXRpbmctbWluX2FnZQ0KDQpgYGB7cn0NCnBsb3RfY29ycmVsYXRpb24obmEub21pdChmaW5hbF9ib2FyZF9nYW1lcyksIG1heGNhdCA9IDVMKQ0KYGBgDQpBaG9yYSBkZSB1bmEgbWFuZXJhIG3DoXMgZGV0YWxsYWRhIHZhbW9zIGEgYW5hbGl6YXIgbGFzIHZhcmlhYmxlcyBtw6FzIGNvcnJlbGFjaW9uYWRhcyBlbnRyZSBzaSwgZXN0ZSB0b3AgZGUgMTA6DQoNCmBgYHtyfQ0KY29ycl9jcm9zcyhmaW5hbF9ib2FyZF9nYW1lcywgIyBuYW1lIG9mIGRhdGFzZXQNCiAgbWF4X3B2YWx1ZSA9IDAuMDUsICMgZGlzcGxheSBvbmx5IHNpZ25pZmljYW50IGNvcnJlbGF0aW9ucyAoYXQgNSUgbGV2ZWwpDQogIHRvcCA9IDEwICMgZGlzcGxheSB0b3AgMTAgY291cGxlcyBvZiB2YXJpYWJsZXMgKGJ5IGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50KQ0KKQ0KYGBgDQojIyMgUVEgcGxvdA0KDQpMYSBncsOhZmljYSBRdWFudGlsZS1RdWFudGlsZSBlcyB1bmEgZm9ybWEgZGUgdmlzdWFsaXphciBsYSBkZXN2aXNpw7NuIGRlIHVuYSBkaXN0cmlidWNpw7NuIGRlIHByb2JhYmlsaWRhZCBlc3BlY8OtZmljYS4NCg0KRGVzcHXDqXMgZGUgYW5hbGl6YXIgZXN0b3MgZ3LDoWZpY29zLCBhIG1lbnVkbyBlcyBiZW5lZmljaW9zbyBhcGxpY2FyIHVuYSB0cmFuc2Zvcm1hY2nDs24gbWF0ZW3DoXRpY2EgKGNvbW8gbG9nYXJpdG1vKSBwYXJhIG1vZGVsb3MgY29tbyBsYSByZWdyZXNpw7NuIGxpbmVhbC4gUGFyYSBoYWNlcmxvLCBwb2RlbW9zIHVzYXIgbGEgZnVuY2nDs24gcGxvdF9xcS4gRGUgZm9ybWEgcHJlZGV0ZXJtaW5hZGEsIHNlIGNvbXBhcmEgY29uIGxhIGRpc3RyaWJ1Y2nDs24gbm9ybWFsLg0KDQpgYGB7cn0NCnFxX2RhdGEgPC0gZmluYWxfYm9hcmRfZ2FtZXNbLCBjKCJtaW5fcGxheXRpbWUiLCAibWF4X3BsYXl0aW1lIiwgIm1pbl9hZ2UiLCAicGxheWluZ190aW1lIiwgImF2ZXJhZ2VfcmF0aW5nIildDQoNCnBsb3RfcXEocXFfZGF0YSwgc2FtcGxlZF9yb3dzID0gMTAwMEwpDQoNCmBgYA0KRW4gZWwgZ3LDoWZpY28sIGxhcyBjb2x1bW5hcyBwYXJlY2VuIHNlc2dhZGFzIGVuIGFtYmFzIGNvbGFzLiBBcGxpcXVlbW9zIHVuYSB0cmFuc2Zvcm1hY2nDs24gbG9nYXLDrXRtaWNhIHNpbXBsZSB5IGdyYWZpcXVlbW9zIGRlIG51ZXZvLiBBdmVyYWdlIFJhdGluZyBubyBtZWpvcsOzLiBHYWJvIHNpIHZlcyBxdWUgZXMgbmVjZXNhcmlvIHBvZGVtb3MgaGFjZXJsZSB1cGRhdGUgYSBsYXMgY29sdW1uYXMgY29uIGxhIHRyYW5zZm9ybWFjacOzbi4NCmBgYHtyfQ0KbG9nX3FxX2RhdGEgPC0gdXBkYXRlX2NvbHVtbnMocXFfZGF0YSwgMTo1LCBmdW5jdGlvbih4KSBsb2coeCArIDEpKQ0KDQoNCnBsb3RfcXEobG9nX3FxX2RhdGEsIHNhbXBsZWRfcm93cyA9IDEwMDBMKQ0KDQpgYGANCg0KIyMjIMOBbmFsaXNpcyBFeHBsb3JhdG9yaW8gZGUgbG9zIERhdG9zDQpUZW5pZW5kbyBudWVzdHJhcyB2YXJpYWJsZXMgY29uIG1heW9yIGNvcnJlbGFjacOzbiB2YW1vcyBhIGdyw6FmaWNhcmxhcyBjb24gZ2VvbSBwb2ludC4uOg0KDQotIG1pbl9wbGF5dGltZS1tYXhfcGxheXRpbWUNCg0KDQpgYGB7cn0NCmZpbmFsX2JvYXJkX2dhbWVzICU+JSAgZ2dwbG90KGFlcyh4ID0gbWluX3BsYXl0aW1lLCB5ID0gbWF4X3BsYXl0aW1lKSkgKyANCiAgZ2VvbV9wb2ludCgpDQpgYGANCg0KLSBhdmVyYWdlX3JhdGluZy1taW5fYWdlDQoNCg0KYGBge3J9DQpmaW5hbF9ib2FyZF9nYW1lcyAlPiUgIGdncGxvdChhZXMoeCA9IGF2ZXJhZ2VfcmF0aW5nLCB5ID0gbWluX2FnZSkpICsgDQogIGdlb21fcG9pbnQoKQ0KYGBgDQoNCi0gYXZlcmFnZV9yYXRpbmctcGxheWluZ190aW1lDQoNCg0KYGBge3J9DQpmaW5hbF9ib2FyZF9nYW1lcyAlPiUgIGdncGxvdChhZXMoeCA9IHBsYXlpbmdfdGltZSwgeSA9IGF2ZXJhZ2VfcmF0aW5nKSkgKyANCiAgZ2VvbV9wb2ludCgpDQpgYGANCg0KLSB1c2Vyc19yYXRlZC1hdmVyYWdlX3JhdGluZw0KDQoNCmBgYHtyfQ0KZmluYWxfYm9hcmRfZ2FtZXMgJT4lICBnZ3Bsb3QoYWVzKHggPSB1c2Vyc19yYXRlZCwgeSA9IGF2ZXJhZ2VfcmF0aW5nKSkgKyANCiAgZ2VvbV9wb2ludCgpDQpgYGANCg0KDQojIyNVc2luZyB2dHJlZSBwYXJhIGV4cGxvcmFyDQoNClVzYW1vcyB2dHJlZSBwYXJhIG9ic2VydmFyIGxhIGNvbmNlbnRyYWNpw7NuIGRlIGxvcyBkYXRvcyBwb3IgZWplbXBsbyBwYXJhIG1pbl9hZ2UsIGRvbmRlIGxhIG1heW9yw61hIGRlIGxvcyBkYXRvcyBzZSBjb25jZW50cmFuIGVuIG1pbl9hZ2UgZGUgOCBhw7FvcywgMTAgYcOxb3MgeSAxMiBhw7Fvcy4NCg0KYGBge3J9DQp2dHJlZShmaW5hbF9ib2FyZF9nYW1lcywgIm1pbl9hZ2UiKQ0KYGBgDQoNClVzYW1vcyB2dHJlZSBwYXJhIG9ic2VydmFyIGxhIGNvbmNlbnRyYWNpw7NuIGRlIGxvcyBkYXRvcyBwb3IgZWplbXBsbyBwYXJhIG1pbl9wbGF5ZXJzLCB0ZW5lbW9zIGNhc2kgdW4gNjklIHBhcmEgbWluIDIganVnYWRvcmVzIHkgY2VyY2EgZGVsIDE5JSBwYXJhIG1pbiAzIGp1Z2Fkb3Jlcy4NCg0KYGBge3J9DQp2dHJlZShmaW5hbF9ib2FyZF9nYW1lcywgIm1pbl9wbGF5ZXJzIikNCmBgYA0KDQoNClVzYW1vcyB2dHJlZSBwYXJhIG9ic2VydmFyIGxhIGNvbmNlbnRyYWNpw7NuIGRlIGxvcyBkYXRvcyBwb3IgZWplbXBsbyBwYXJhIG1heF9wbGF5ZXJzLCB0ZW5lbW9zIGNhc2kgdW4gMjMlIHBhcmEgbcOheCA0IGp1Z2Fkb3JlcyB5IGNlcmNhIGRlbCAyNSUgcGFyYSBtw6F4IDYganVnYWRvcmVzLg0KDQpgYGB7cn0NCnZ0cmVlKGZpbmFsX2JvYXJkX2dhbWVzLCAibWF4X3BsYXllcnMiKQ0KYGBgDQoNCg0KDQoNCg0KTm90ZSB0aGF0IHRoZSBgZWNobyA9IEZBTFNFYCBwYXJhbWV0ZXIgd2FzIGFkZGVkIHRvIHRoZSBjb2RlIGNodW5rIHRvIHByZXZlbnQgcHJpbnRpbmcgb2YgdGhlIFIgY29kZSB0aGF0IGdlbmVyYXRlZCB0aGUgcGxvdC4NCg==